#include "task.h"

#include <stdio.h>
#include <string.h>
#include "ARMCM3.h"
#include "os.h"

TASK_T              *curTask;                           /* 当前任务*/
TASK_T              *nextTask;                          /* 下一个任务*/

static    BITMAP_T  taskReadyBitmap;                    /* 就绪任务位图*/
static    LIST_T    taskReadyList[OS_PRIO_COUNT];       /* 任务就绪队列*/

static    LIST_T    taskDelayList;                      /* 任务延时队列*/

volatile  uint8_t   schedLockCount;                     /* 调度锁 */


/* 空闲任务----在所有用户任务都阻塞时，工作的任务*/
TASK_T taskIdle;
tTaskStack taskIdleEnv[OS_IDLE_STACK_SIZE];

__weak void idleTaskEntry(void *param)
{
    for(;;)
    {
        
#if OS_ENABLE_HOOKS == 1
        vHooksCpuIdle();
#endif // OS_ENABLE_HOOKS == 1
    }
}







/*****************************************************************************
 * @function        vTaskCreate
* @brief 			线程任务创建.
 *
 * 此函数用于创建一个任务线程
 *  
 * @param[in,out] 	task 任务控制块句柄.
 * @param[in]       entry 任务回调函数.
 * @param[in] 	    param 任务回调函数的形参.
 * @param[in] 	    stack 用于存放任务堆栈的指针.
 * 
 * @return 			None.
 *
 * ***************************************************************************/
void vTaskCreate(TASK_T *task, void (*entry)(void *), void *param, uint32_t prio, tTaskStack *stack, uint32_t stackSize)
{
    uint32_t *stackTop;
    
    task->stackBase = stack;                        /* 设置堆栈基地址*/
    task->stackSize = stackSize;                    /* 设置堆栈空间大小*/
    /* 初始化堆栈空间*/
    memset(stack, 0x00, stackSize);
    
    stackTop = stack + (stackSize / sizeof(tTaskStack));
   
    /* 初始化任务堆栈数据*/
    *(--stackTop) = (unsigned long)(1 << 24);      /* 使用Thumb指令集*/
    *(--stackTop) = (unsigned long)entry;          /* 程序入口*/    
    *(--stackTop) = (unsigned long)0x14;
    *(--stackTop) = (unsigned long)0x12;
    *(--stackTop) = (unsigned long)0x3;
    *(--stackTop) = (unsigned long)0x2;
    *(--stackTop) = (unsigned long)0x1;
    *(--stackTop) = (unsigned long)param;          /* R0默认会接收第一个形参*/
    
    /*R13去哪了？---R13是SP堆栈指针，无需关注  
     *这些硬件自动加载和保护的寄存器，怎么跟我们的任务堆栈数据联系?
     * --------通过与PSP进程堆栈指针进行绑定*/
    
    *(--stackTop) = (unsigned long)0x11;
    *(--stackTop) = (unsigned long)0x10;
    *(--stackTop) = (unsigned long)0x09;
    *(--stackTop) = (unsigned long)0x08;
    *(--stackTop) = (unsigned long)0x07;
    *(--stackTop) = (unsigned long)0x06;
    *(--stackTop) = (unsigned long)0x05;
    *(--stackTop) = (unsigned long)0x04;
    task->stack = stackTop;                    // 初始化任务堆栈
    
    task->prio = prio;                      // 设置任务的优先级
    task->slice = OS_SLICE_MAX;             // 设置时间片
    
    vNodeInit(&task->taskLinkNode);        // 初始化就绪队列节点
    vTaskReadyListAdd(task);              // 将任务添加进就绪队列中
    
    vNodeInit(&task->taskDelayNode);        // 初始化延时队列节点
    task->delayTicks = 0;                   // 初始化任务的延时节拍
    
    task->suspendCount = 0;                 // 初始化挂起次数
    
    task->clean = (void (*)(void *))0;      // 初始化清除函数
    task->cleanParam = (void *)0;           // 初始化清除函数形参
    task->requestDeleteFlag = 0;            // 初始化请求删除任务标志
    
#if OS_ENABLE_HOOKS == 1
    vHooksTaskInit(task);
#endif // OS_ENABLE_HOOKS == 1
    
}


/*****************************************************************************
 * @function        vTaskSchedInit
 * @brief 			初始化任务调度器相关功能.
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void vTaskSchedInit(void)
{
    int i ;
    /* 初始化就绪任务位图*/
    vBitmapInit(&taskReadyBitmap);
    
    /* 初始化就绪队列*/
    for (i = 0; i < OS_PRIO_COUNT; i++)
    {
        vListInit(&taskReadyList[i]);
    }
    /* 初始化延时队列*/
    vListInit(&taskDelayList);
    /* 初始化调度锁*/
    schedLockCount = 0;

#if OS_ENABLE_TIMER == 1
    /* 初始化软件定时器*/
    vTimerModuleInit();
#endif // OS_ENABLE_TIMER == 1
    
    /* 创建空闲任务*/
    vTaskCreate(&taskIdle, idleTaskEntry, 0x00000000, OS_IDLE_PRIO , taskIdleEnv, sizeof(taskIdleEnv));
}


/*****************************************************************************
 * @function        vTaskStartScheduler
* @brief 			启动任务调度.
 *
 * 此函数用于启动任务调度器，以运行多线程任务。
 * 
 * @return 			None.
 *
 * ***************************************************************************/
void vTaskStartScheduler(void)
{
    /* 用户级任务堆栈*/
    __set_PSP(0); 
    
    /* 设置pendsv的优先级*/
    os_setPendSV_PRI();
    
    /* 启动首个任务--优先级最高的任务*/
    nextTask = vTaskHighestReady();
    
    /* 进行任务调度*/
    os_taskSwitch();
}


/*****************************************************************************
 * @function        vTaskYield
* @brief 			主动让出CPU，实现线程轮转.
 *
 * 此函数用于让出CPU权限，供其他线程轮转
 * 
 * @return 			None.
 *
 * ***************************************************************************/
void vTaskYield(void)
{
    TASK_T *tempTask;
    //
    /* 对谁保护???-----对 taskTable进行保护*/
    /* 为什么要进行临界区保护???虽然systick中断、线程延时中会调用.
     * -----防止其他中断修改taskTable或 curTask，导致nexttask错误*/
    
    // 开启临界区保护
    uint32_t primask = vTaskEnterCritical();
    
    
    /* 调度锁保护*/
    if (schedLockCount > 0)
    {
        vTaskExitCritical(primask);
        return ;
    }
    
    /* 正常任务切换--优先级排序*/
    tempTask = vTaskHighestReady();
    if (tempTask != curTask)
    {
        nextTask = tempTask;
#if OS_ENABLE_HOOKS == 1
        vHooksTaskSwitch(curTask, nextTask);
#endif // OS_ENABLE_HOOKS == 1
        
        /* 进行上下文切换*/
        os_taskSwitch();
    }
    
    // 关闭临界区保护
    vTaskExitCritical(primask);
}


/*****************************************************************************
 * @function        vTaskSystemTickHandle
 * @brief 			系统心跳处理函数--中断服务程序中运行.
 *
 * 此函数用于操作系统中心跳节拍处理
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void vTaskSystemTickHandle(void)
{
    NODE_T *node;
    TASK_T *task;
    
    uint32_t primask = vTaskEnterCritical();
    
    /* 就绪队列中有多个同优先级任务，时间片轮询*/
    if (--curTask->slice == 0)
    {
        // 同优先级队列中有多个任务
        if (vListCount(&taskReadyList[curTask->prio]) > 1)
        {
            /* 将当前任务从就绪队列中删除*/
            vListRemoveFirst(&taskReadyList[curTask->prio]);
            /* 并将当前任务插入到就绪队列的末尾*/
            vListAddLast(&taskReadyList[curTask->prio], &(curTask->taskLinkNode));
        }
        /* 重置计数器*/
        curTask->slice = OS_SLICE_MAX;
    }
    
    
    /* 延时队列任务计时*/
    for (node = taskDelayList.headNode.nextNode; node != &(taskDelayList.headNode); node = node->nextNode)
    {
        task = container_of(node, TASK_T, taskDelayNode); 
        if (--task->delayTicks == 0)
        {
            /* 任务处于事件等待队列中， 将任务从等待队列中唤醒*/
            if (task->waitEvent)
            {
                /* 删除事件队列中的任务， 此时消息为空， 等待结果为timeout*/
                vEventRemoveTask(task, (void *)0, OS_TIMEOUT);
            }
            
            /* 延时节拍完成，进入就绪态*/
            vTaskDelayListDel(task);
            vTaskReadyListAdd(task);
        }
    }
    
    vTaskExitCritical(primask);
    
#if OS_ENABLE_TIMER == 1
    // 软件定时器模块处理程序
    vTimerModuleNotify();
#endif // OS_ENABLE_TIMER == 1
    
    
#if OS_ENABLE_HOOKS == 1
    vHooksSysTick();
#endif // OS_ENABLE_HOOKS == 1
    
    /* 在滴答过程中，有任务可能延时完毕，可进行任务调度*/
    vTaskYield();
}



/*****************************************************************************
 * @function        vTaskGetInfo
 * @brief 			获取任务的相关状态信息.
 *  
 * @param[in] 		task 任务控制块.
 * @param[out]      info 任务信息块.
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void     vTaskGetInfo(TASK_T *task, TASK_INFO_T *info)
{
#if OS_ENABLE_TRACE_STACK == 1
    uint32_t *stackEnd;
#endif // OS_ENABLE_TRACE_STACK == 1
    
    uint32_t primask = vTaskEnterCritical();
    
    info->delayTicks = task->delayTicks;
    info->prio = task->prio;
    info->slice = task->slice;
    info->state = task->state;
    info->suspendCount = task->suspendCount;
    
    info->stackSize = task->stackSize;
    
    
#if OS_ENABLE_TRACE_STACK == 1
    info->stackFree = 0;
    stackEnd = task->stackBase;
    while ((*stackEnd++ == 0) &&  (stackEnd < (task->stackBase + (task->stackSize / sizeof(tTaskStack)))))
    {
        info->stackFree++;
    }
    info->stackFree *= sizeof(tTaskStack);
#endif // OS_ENABLE_TRACE_STACK == 1
 
    vTaskExitCritical(primask);
}


/*****************************************************************************
 * @function        vTaskHighestReady
 * @brief 			获取优先级最高的就绪任务.
 * 
 * @return 			返回优先级最高的就绪任务.	
 *
 * ***************************************************************************/
TASK_T * vTaskHighestReady(void)
{
    NODE_T *node;
    uint32_t prio;
    
    /* 获取最高优先级的任务队列序号*/
    prio = vBitmapGetFirstSet(&taskReadyBitmap);
    /* 获取任务队列中首个节点*/
    node = taskReadyList[prio].headNode.nextNode;
    
    /* 利用container_of 获取任务控制块*/
    return container_of(node, TASK_T, taskLinkNode);
}



/*****************************************************************************
 * @function        sf_taskReadyAdd
 * @brief 			将任务添加进就绪队列. 会将任务添加到taskTable中，并设置就绪位图中对应位为1,
 *                  任务状态为就绪态
 *  
 * @param[in] 		task 要添加的任务控制块.
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void vTaskReadyListAdd(TASK_T *task)
{
    /* 保护 taskReadyList 和TaskReadyBitmap*/
    uint32_t primask = vTaskEnterCritical();
    
    /* 将任务添加至就绪队列*/
    vListAddLast(&taskReadyList[task->prio], &(task->taskLinkNode));
    /* 更新就绪位图*/
    vBitmapSet(&taskReadyBitmap, task->prio);
    task->state |= OS_TASK_STA_READY;
    
    vTaskExitCritical(primask);
}

/*****************************************************************************
 * @function        vTaskReadyListDel
 * @brief 			任务退出就绪队列. 会将任务从taskTable中删除，并设置就绪位图中对应位为0,
 *                  任务退出就绪态.
 *  
 * @param[in] 		task 要退出的任务控制块.
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void vTaskReadyListDel(TASK_T *task)
{
    /* 保护 taskReadyList 和TaskReadyBitmap*/
    uint32_t primask = vTaskEnterCritical();
    
    /* 将任务从就绪队列中删除*/
    vListRemove(&taskReadyList[task->prio], &(task->taskLinkNode));
    
    /* 当就绪队列中无节点时，清除就绪位图*/
    if (vListCount(&taskReadyList[task->prio]) == 0)
    {
        vBitmapClear(&taskReadyBitmap, task->prio);
    }
    task->state &= ~(OS_TASK_STA_READY);
    
    vTaskExitCritical(primask);
}


/*****************************************************************************
 * @function        vTaskDelayListAdd
 * @brief 			任务进入延时队列. 将任务添加进延时队列链表，设置延时节拍数，并修改任务状态为阻塞态
 *  
 * @param[in] 		task 要进入阻塞队列的任务.
 * @param[in]       ticks 阻塞的节拍数.
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void vTaskDelayListAdd(TASK_T *task, uint32_t ticks)
{   
    task->delayTicks = ticks;
    
    vListAddFirst(&taskDelayList, &(task->taskDelayNode));
    task->state |= OS_TASK_STA_DELAY;
}

/*****************************************************************************
 * @function        vTaskDelayListAdd
 * @brief 			任务退出延时队列. 将任务从延时队列链表删除，任务退出阻塞态
 *  
 * @param[in] 		task 要退出阻塞队列的任务.
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void vTaskDelayListDel(TASK_T *task)
{
    vListRemove(&taskDelayList, &(task->taskDelayNode));
    task->state &= ~(OS_TASK_STA_DELAY);
}




/*****************************************************************************
 * @function        vTaskDelay
 * @brief 			线程级延时.
 *
    * 此函数用于在线程中进行延时操作，非硬延时，延时期间其他线程可以工作
 *  
 * @param[in] 		ticks 延时的节拍数
 * 
 * @return 			None.
 *
 * ***************************************************************************/
void vTaskDelay(uint32_t ticks)
{

    uint32_t primask = vTaskEnterCritical();
    
    /* 任务退出就绪队列*/
    vTaskReadyListDel(curTask);
    /* 任务进入阻塞队列*/
    vTaskDelayListAdd(curTask, ticks);
    
    vTaskExitCritical(primask);
    
    /* 延时配置完成后，进行任务切换，切换至其他任务或者空闲任务*/
    vTaskYield();
}


/*****************************************************************************
 * @function        vTaskSchedDisable
 * @brief 			开启调度锁，关闭任务调度.
 *
 * 此函数用于开启调度锁，关闭任务调度
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void vTaskSchedDisable(void)
{
    uint32_t primask = vTaskEnterCritical();
    
    if (schedLockCount < 255)
    {
        schedLockCount++;
    }
    
    vTaskExitCritical(primask);
}


/*****************************************************************************
 * @function        vTaskSchedEnable
 * @brief 			关闭调度锁，开启任务调度.
 *
 * 此函数用于关闭调度锁，开启任务调度.
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void vTaskSchedEnable(void)
{
    uint32_t primask = vTaskEnterCritical();
    
    if (schedLockCount > 0)
    {
        if (--schedLockCount == 0)
        {
            // 由于关闭了任务调度，其他任务可能已进入就绪态。
            vTaskYield();
        }
    }
    vTaskExitCritical(primask);
}


/*****************************************************************************
 * @function        vTaskSuspend
 * @brief 			任务挂起. 对处于就绪态/运行态的程序进行挂起（阻塞态除外）。
 *  
 * @param[in] 		task 待挂起的任务.
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void     vTaskSuspend(TASK_T *task)   
{
    uint32_t primask = vTaskEnterCritical();
    
    /* 非阻塞态*/
    if ( !(task->state & OS_TASK_STA_DELAY))
    {
        if (++task->suspendCount <= 1)
        {
            /* 将任务从就绪队列中删除*/
            vTaskReadyListDel(task);
            /* 进入挂起态*/
            task->state |= OS_TASK_STA_SUSPEND;
            
            /* 要挂起的任务还是运行态时，进行任务调度*/
            if (task == curTask)
            {
                vTaskYield();
            }
        }
    }
        
    vTaskExitCritical(primask);
}

/*****************************************************************************
 * @function        vTaskResume
 * @brief 			任务恢复. 对处于挂起态的程序恢复到就绪态。
 *  
 * @param[in] 		task 待恢复的任务.
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void     vTaskResume(TASK_T *task)
{
    uint32_t primask = vTaskEnterCritical();
    
    /* 当前任务处于挂起态*/
    if (task->state & OS_TASK_STA_SUSPEND)
    {
        if (--task->suspendCount == 0)
        {
            /* 清除挂起标志*/
            task->state &= ~(OS_TASK_STA_SUSPEND);
            /* 将任务添加至就绪队列*/
            vTaskReadyListAdd(task);
            /* 启动任务调度*/
            vTaskYield();
        }
    }
    
    vTaskExitCritical(primask);
}


/*****************************************************************************
 * @function        vTaskSetCleanCallFunc
 * @brief 			设置任务的清理回调函数.
 *  
 * @param[in] 		task 待设置清理回调函数的任务.
 * @param[in]       clean 回调函数指针.
 * @param[in] 	    param 回调函数形参.
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void     vTaskSetCleanCallFunc(TASK_T *task, void (*clean)(void *), void *param)
{
    uint32_t primask = vTaskEnterCritical();
   
    task->clean = clean;
    task->cleanParam = param;
    
    vTaskExitCritical(primask);
}


/*****************************************************************************
 * @function        vTaskRequestDelete
 * @brief 			请求删除指定任务.
 *  
 * @param[in] 		task 请求要删除的任务.
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void     vTaskRequestDelete(TASK_T *task)
{
    uint32_t primask = vTaskEnterCritical();
    
    task->requestDeleteFlag = 1;
    
    vTaskExitCritical(primask);
}

/*****************************************************************************
 * @function        vTaskIsRequestDelete
 * @brief 			检查是否有删除自身任务请求.
 * 
 * @return 			0--无删除请求, 1--有删除请求.	
 *
 * ***************************************************************************/
uint8_t  vTaskIsRequestDelete(void)
{
    uint8_t del = 0;
    uint32_t primask = vTaskEnterCritical();
    
    del = curTask->requestDeleteFlag;
    
    vTaskExitCritical(primask);
    
    return del;
}

/*****************************************************************************
 * @function        vTaskDeleteSelf
 * @brief 			删除当前任务.
 * 
 * @return 			None.	
 *
 * ***************************************************************************/
void     vTaskDeleteSelf(void)
{
    uint32_t primask = vTaskEnterCritical();
    
    /* 将当前任务从就绪队列中删除*/
    vTaskReadyListDel(curTask);
    
    /* 若存在清理函数，则调用清理函数进行清理*/
    if (curTask->clean)
    {
        curTask->clean(curTask->cleanParam);
    }
    
    /* 任务调度切换任务*/
    vTaskYield();    
    
    vTaskExitCritical(primask);
}
    

/*****************************************************************************
 * @function        vTaskForceDelete
 * @brief 		    强行删除指定任务.
 * 
 * @param[in]       task 要被删除的任务.
 * @return 			None.	
 *
 * ***************************************************************************/
void     vTaskForceDelete(TASK_T *task)
{
    uint32_t primask = vTaskEnterCritical();
    
    // 若任务处于就绪态, 将任务从就绪队列中删除
    if (task->state & OS_TASK_STA_READY)
    {
        vTaskReadyListDel(task);
    }
    // 若任务处于阻塞态，将任务从阻塞队列中删除
    else if (task->state & OS_TASK_STA_DELAY)
    {
        /* 若任务还处于事件等待队列中， 将任务从等待队列中唤醒*/
        if (task->waitEvent)
        {
            /* 删除事件队列中的任务， 此时消息为空， 等待结果为强制删除*/
            vEventRemoveTask(task, (void *)0, OS_FORCE_DEL);
        }
        
        vTaskDelayListDel(task);
    }
    
    /* 若存在清理函数，则调用清理函数进行清理*/
    if (task->clean)
    {
        task->clean(task->cleanParam);
    }
    
    // 若任务处于运行态
    if (task == curTask)
    {
        /* 进行任务调度，切换任务*/
        vTaskYield();  
    }
    vTaskExitCritical(primask);
}


